/*
* Conditions Of Use
*
* This software was developed by employees of the Sigmatrix(Beijing) Corporation.
* This software is provided by sigmatrix as a service and is expressly
* provided "AS IS."  Sigmatrix MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY.  Sigmatrix does not warrant or make any representations
* regarding the use of the software or the results thereof, including but
* not limited to the correctness, accuracy, reliability or usefulness of
* the software.
*
* Permission to use this software is contingent upon your acceptance
* of the terms of this agreement.
*
*/
package com.sigmatrix.h5.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSONObject;
import com.sigmatrix.h5.dao.InStorageDao;
import com.sigmatrix.h5.dao.OutStorageDao;
import com.sigmatrix.h5.dao.OutStorageDetailDao;
import com.sigmatrix.h5.dao.OutStorageTerminalUserDao;
import com.sigmatrix.h5.dao.ProductDao;
import com.sigmatrix.h5.dao.WarehouseDao;
import com.sigmatrix.h5.dto.DisDealersData;
import com.sigmatrix.h5.dto.DisTerminalShopData;
import com.sigmatrix.h5.dto.OutStorageData;
import com.sigmatrix.h5.dto.OutStorageDetailData;
import com.sigmatrix.h5.dto.PackStandardData;
import com.sigmatrix.h5.dto.ProductData;
import com.sigmatrix.h5.dto.QRCodeError;
import com.sigmatrix.h5.dto.WarehouseData;
import com.sigmatrix.h5.entity.FirstLevelQRCode;
import com.sigmatrix.h5.entity.InStorage;
import com.sigmatrix.h5.entity.MultistageLevelQRCode;
import com.sigmatrix.h5.entity.OutStorage;
import com.sigmatrix.h5.entity.OutStorageDetail;
import com.sigmatrix.h5.entity.OutStorageTerminalUser;
import com.sigmatrix.h5.entity.Product;
import com.sigmatrix.h5.entity.SyncTask;
import com.sigmatrix.h5.enums.MultistageLevelQRCodeStatusEnum;
import com.sigmatrix.h5.enums.OutStorageStatusEnum;
import com.sigmatrix.h5.enums.ResponseEnum;
import com.sigmatrix.h5.exception.DaoException;
import com.sigmatrix.h5.exception.ServiceException;
import com.sigmatrix.h5.page.PageBean;
import com.sigmatrix.h5.request.Request;
import com.sigmatrix.h5.response.Response;
import com.sigmatrix.h5.service.CodeCirculationService;
import com.sigmatrix.h5.service.DealerService;
import com.sigmatrix.h5.service.DisTerminalShopService;
import com.sigmatrix.h5.service.FirstLevelQrCodeService;
import com.sigmatrix.h5.service.InStorageService;
import com.sigmatrix.h5.service.MultistageLevelQrCodeService;
import com.sigmatrix.h5.service.OutStorageDetailService;
import com.sigmatrix.h5.service.OutStorageService;
import com.sigmatrix.h5.service.ProductService;
import com.sigmatrix.h5.service.SyncTaskService;
import com.sigmatrix.h5.service.WarehouseService;
import com.sigmatrix.h5.service.WarehouseStockDetailsService;
import com.sigmatrix.h5.service.WarehouseStockService;
import com.sigmatrix.h5.service.common.impl.BaseServiceImpl;
import com.sigmatrix.h5.utils.Cons;
import com.sigmatrix.h5.utils.DateUtil;
import com.sigmatrix.h5.utils.QRCodeUtil;
import com.sigmatrix.h5.utils.ThreadPool;
import com.sigmatrix.h5.utils.Utils;
import com.sigmatrix.h5.web.vo.BaseOutStorageVo;
import com.sigmatrix.h5.web.vo.InStorageAddVo;
import com.sigmatrix.h5.web.vo.InStorageAutoAddVo;
import com.sigmatrix.h5.web.vo.InStorageDetailAddVo;
import com.sigmatrix.h5.web.vo.InStorageDetailAutoAddVo;
import com.sigmatrix.h5.web.vo.InStorageUpLoadAutoDetailVo;
import com.sigmatrix.h5.web.vo.InStorageUpLoadDetailVo;
import com.sigmatrix.h5.web.vo.InStorageUpLoadVo;
import com.sigmatrix.h5.web.vo.OperateCodeUpLoadVo;
import com.sigmatrix.h5.web.vo.OperateCodeVo;
import com.sigmatrix.h5.web.vo.OutStorageDetailVo;
import com.sigmatrix.h5.web.vo.OutStorageVo;

/**
 *@ClassName: OutStorageServiceImpl
 *@Description: 出库单业务层接口实现
 *@author youjun
 *@date 2017年6月23日 上午9:45:42
 */
@Service(value="outStorageService")
public class OutStorageServiceImpl extends BaseServiceImpl<OutStorageData> implements OutStorageService {

	/**logger record*/
	private static final Logger LOGGER = LoggerFactory.getLogger(OutStorageServiceImpl.class);
	
	/**出库单数据库操作接口*/
	@Autowired
	private transient OutStorageDao outStorageDao;
	
	/**出库单详情业务层接口*/
	@Autowired
	private transient OutStorageDetailService outStorageDetailService;
	
	/**经销商信息业务层接口*/
	@Autowired
	private transient DealerService dealersService;
	
	/**仓库信息业务层接口*/
	@Autowired
	private transient WarehouseService warehouseService;
	
	/**仓库信息 */
	@Autowired
	private transient WarehouseDao warehouseDao;
	
	/**出库单详情数据库操作接口*/
	@Autowired
	private transient OutStorageDetailDao outStorageDetailDao;
	
	/**产品业务层接口*/
	@Autowired
	private transient ProductService productService;
	
	/**多级码业务层接口*/
	@Autowired
	private transient MultistageLevelQrCodeService multistageLevelQRCodeService;
	
	/**一级码业务层接口*/
	@Autowired
	private transient FirstLevelQrCodeService firstLevelQRCodeService;
	
	/**出库单绑定用户接口 */
	@Autowired
	private transient OutStorageTerminalUserDao outStorageTerminalUserDao;
	
	/**库存业务层接口*/
	@Autowired
	private transient WarehouseStockService warehouseStockService;
	
	/**库存详情业务层接口*/
	@Autowired
	private transient WarehouseStockDetailsService warehouseStockDetailsService;
	
	@Autowired
	private transient SyncTaskService syncTaskService;
	
	@Autowired
	private transient ProductDao productDao;
	
	@Autowired
	private transient InStorageDao inStorageDao;
	
	@Autowired
	private transient InStorageService inStorageService;
	
	/**码流转记录业务层接口*/
	@Autowired
	private transient CodeCirculationService codeCirculationService;
	
	@Autowired
	private DisTerminalShopService disTerminalShopService;
	
	/**能执行上传出库单操作的状态：已审核、进行中*/
	private static List<Integer> canUpdateStatusList = new ArrayList<Integer>();
	
	/**能执行关闭出库单操作的状态:进行中、有异常*/
	private static List<Integer> canCloseStatusList = new ArrayList<Integer>();
	
	/**能执行关闭出库单详情单操作的状态:进行中、有异常*/
	private static List<Integer> canCloseDetailStatusList = new ArrayList<Integer>();
	
	/**多级码已经出库的状态集合*/
	private static List<Integer> alreadyOutStorageStatusList = new ArrayList<Integer>();
	
	static {
		canUpdateStatusList.add(OutStorageStatusEnum.HAS_AUDIT.getId()); // 已审核
		canUpdateStatusList.add(OutStorageStatusEnum.PROCEED_ING.getId()); // 进行中
		
		alreadyOutStorageStatusList.add(MultistageLevelQRCodeStatusEnum.SALES_OUT_STORAGE.getId());
		alreadyOutStorageStatusList.add(MultistageLevelQRCodeStatusEnum.REWORK_OUT_STORAGE.getId());
		alreadyOutStorageStatusList.add(MultistageLevelQRCodeStatusEnum.TRANSFERS_OUT_STORAGE.getId());
		alreadyOutStorageStatusList.add(MultistageLevelQRCodeStatusEnum.OTHER_OUT_STORAGE.getId());
		
		canCloseStatusList.add(OutStorageStatusEnum.PROCEED_ING.getId()); // 进行中
		canCloseStatusList.add(OutStorageStatusEnum.EXCEPTION.getId()); // 有异常
		canCloseStatusList.add(OutStorageStatusEnum.HAS_AUDIT.getId()); // 已审核
		
		canCloseDetailStatusList.add(OutStorageStatusEnum.HAS_AUDIT.getId()); // 已审核
		canCloseDetailStatusList.add(OutStorageStatusEnum.PROCEED_ING.getId()); // 进行中
		canCloseDetailStatusList.add(OutStorageStatusEnum.EXCEPTION.getId()); // 有异常
	}
	
	@Override
	public Response findListByPage(final PageBean<OutStorageData> pageBean, final OutStorageVo outStorageVo, final boolean isQueryDetail) throws ServiceException {
		
		if(outStorageVo==null) {
			throw new ServiceException("传递的出库单对象不能为空");
		}
		try {
			final OutStorage outStorage = new OutStorage();
			final Map<String, Object> otherParamsMap = new HashMap<String, Object>();
			final String enterpriseCode = outStorageVo.getEnterpriseCode();
			BeanUtils.copyProperties(outStorageVo, outStorage);
			//outStorage.setDealerId(outStorageVo.getUserDealerId());//经销商id
			outStorage.setSendDealerId(outStorageVo.getUserDealerId());
			
			final String createTimeStart = outStorageVo.getCreateTimeStart();
			final String createTimeEnd = outStorageVo.getCreateTimeEnd();
			if(StringUtils.isNotBlank(createTimeStart)) {
				otherParamsMap.put("createTimeStart", createTimeStart);
			}
			// 2017-07-11 >> 2017-07-11 23:59:59
			if(StringUtils.isNotBlank(createTimeEnd)&&createTimeEnd.length()>=10) {
				final StringBuilder createTimeEndSb = new StringBuilder();
				createTimeEndSb.append(createTimeEnd.substring(0, 10));
				createTimeEndSb.append(" ").append("23:59:59");
				otherParamsMap.put("createTimeEnd", createTimeEndSb.toString());
			}
		/*	final List<OutStorage> list = outStorageDao.findByPage(pageBean.getPageNo(), pageBean.getPageSize(),
					outStorage, otherParamsMap, enterpriseCode);
			final int total = outStorageDao.getCountByCondition(outStorage, otherParamsMap, enterpriseCode);
			*/
			final List<OutStorage> list = outStorageDao.findByPage(pageBean.getPageNo(), pageBean.getPageSize(),
					outStorage, otherParamsMap, enterpriseCode,outStorageVo.getUserId());
			final int total = outStorageDao.getCountByCondition(outStorage, otherParamsMap, enterpriseCode,outStorageVo.getUserId());
			List<OutStorageData> dataList = convertDataList(list);
			dataList = fillDataList(dataList, outStorageVo.getEnterpriseCode(), true);
			convertPageBean(dataList, total, pageBean);
			return new Response(ResponseEnum.SUCCESS, pageBean);
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}
	
	/**
	 *@Title: convertDataList
	 *@Description: 转为为DTO数据
	 *@return List<OutStorageData>
	 *@author youjun
	 *@date 2017年7月8日 上午11:24:38
	 *@param list
	 *@return
	 */
	private List<OutStorageData> convertDataList(final List<OutStorage> list) {

		if(!CollectionUtils.isEmpty(list)) {
			final List<OutStorageData> dataList = new ArrayList<OutStorageData>();
			OutStorageData data = null;
			for(final OutStorage item:list) {
				data = new OutStorageData();
				BeanUtils.copyProperties(item, data);
				data.setOutStorageId(item.getId());
				data.setDelegatedDealerId(item.getTargetChannelId());//目标经销商
				data.setEntryWarehouseId(item.getToWarehouseId());//目标仓库
				// 处理日期
				if(item.getCreateTime()!=null) {
					data.setCreateTime(DateUtil.getDateTimeStr(item.getCreateTime()));
				}
				dataList.add(data);
			}
			return dataList;
		}
		return null;
	}

	/**
	 * (non-Javadoc)
	 *@Title: getOutStorageList
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.OutStorageService#getOutStorageList(com.sigmatrix.sc.handheld.inter.web.vo.OutStorageVo, boolean)
	 *@author youjun
	 *@date 2017年6月23日 上午9:58:34
	 */
	@Override
	public Response getOutStorageList(final OutStorageVo outStorageVo, final boolean isQueryDetail) throws ServiceException {

		// 参数验证
		final Response validateResponse = validateParams(outStorageVo);
		LOGGER.info("参数验证结果:{}", JSONObject.toJSONString(validateResponse));
		if(validateResponse.getCode().intValue()!=Cons.RESPONSE_CODE_SUCCESS) {
			return validateResponse;
		}
		List<OutStorageData> dataList = new ArrayList<OutStorageData>();
		final OutStorage outStorage = new OutStorage();
		BeanUtils.copyProperties(outStorageVo, outStorage);
		//outStorage.setDealerId(outStorageVo.getUserDealerId());//添加经销商信息
		outStorage.setSendDealerId(outStorageVo.getUserDealerId());
		try {
			//final List<OutStorageData> outStorageList = outStorageDao.findPartColumnListByCondition(outStorage, outStorageVo.getEnterpriseCode());
			final List<OutStorageData> outStorageList = outStorageDao.findPartColumnListByCondition(outStorage, outStorageVo.getEnterpriseCode(), 
					true, outStorageVo.getUserId());
			dataList = fillDataList(outStorageList, outStorageVo.getEnterpriseCode(), isQueryDetail);
			LOGGER.info("查询的出库单列表为:{}", JSONObject.toJSONString(outStorageList));
		} catch (DaoException e) {
			LOGGER.error("getOutStorageList fail", e);
			throw new ServiceException(e);
		}
		return new Response(ResponseEnum.SUCCESS, dataList);
	}

	/**
	 *@Title: validateParams
	 *@Description: 参数验证
	 *@return Response
	 *@author youjun
	 *@date 2017年6月27日 下午3:30:19
	 *@param outStorageVo
	 *@return
	 */
	private Response validateParams(final OutStorageVo outStorageVo) {
		return validateBasicParams(outStorageVo);
	}

	/**
	 *@Title: fillDataList
	 *@Description: 填充出库单
	 *@return List<OutStorageData>
	 *@author youjun
	 *@date 2017年6月25日 下午2:01:56
	 *@param outStorageList
	 * @param isQueryDetail 是否填充详情单
	 *@return
	 * @throws ServiceException 
	 */
	private List<OutStorageData> fillDataList(final List<OutStorageData> outStorageList, final String enterpriseCode, final boolean isQueryDetail) throws ServiceException {

		if(!CollectionUtils.isEmpty(outStorageList)) {
			final Map<Integer, DisDealersData> dealersMap = new HashMap<Integer, DisDealersData>();
			//TODO
			final Map<Integer, WarehouseData> warehouseMap = warehouseService.getAllWarehouseMap(enterpriseCode);
			DisDealersData dealers = null;
			DisDealersData delegatedDealer = null;
			WarehouseData warehouse = null;
			WarehouseData entryWarehouse = null;
			for(final OutStorageData item:outStorageList) {
				// 填充对象信息
				//2代表经销商级别dealerId代表经销商id   3代表终端门店dealerId代表门店id    
				if (Utils.isNotEmpty(item.getCustomerType())) {
					if (item.getCustomerType() == Cons.TAG_LEVEL_STORE) {
						DisTerminalShopData disTerminalShopData = disTerminalShopService.findById(item.getDealerId(), enterpriseCode);
						item.setDealerName(disTerminalShopData!=null?disTerminalShopData.getName():"");
					}
				}
				if (Utils.isNotEmpty(item.getCustomerType())) {
					if (item.getCustomerType() == Cons.TAG_LEVEL_DEALER) {
						dealers = dealersService.getDealersFromMap(dealersMap, item.getDealerId(), enterpriseCode);
						item.setDealerName(dealers!=null?dealers.getDealerName():"");
					}
				}
				delegatedDealer = dealersService.getDealersFromMap(dealersMap, item.getDelegatedDealerId(), enterpriseCode);
				//TODO
				warehouse = warehouseService.getWarehouseFromMap(warehouseMap, item.getWarehouseId(), enterpriseCode);
				entryWarehouse = warehouseService.getWarehouseFromMap(warehouseMap, item.getEntryWarehouseId(), enterpriseCode);
				
				item.setDelegatedDealerName(delegatedDealer!=null?delegatedDealer.getDealerName():"");
				if (delegatedDealer!=null) {
					item.setDelegatedDealerId(delegatedDealer.getDealerId());
				}
				item.setWarehouseName(warehouse!=null?warehouse.getWarehouseName():"");
				item.setEntryWarehouseName(entryWarehouse!=null?entryWarehouse.getWarehouseName():"");
				if (entryWarehouse!=null) {
					item.setEntryWarehouseId(entryWarehouse.getWarehouseId());
				}
			}
			if(isQueryDetail) {
				// 使用两条主sql方式查询出库单集合和对应详情单列表集合,再行组合
				final OutStorageDetailVo outStorageDetailVo = new OutStorageDetailVo();
				outStorageDetailVo.setEnterpriseCode(enterpriseCode);
				final List<OutStorageDetailData> detailList = outStorageDetailService.findPartColumnListByCondition(outStorageDetailVo);
				final List<OutStorageDetailData> combinationDetailList = combinationDetailList(detailList);
				LOGGER.info("查询的出库单对应的详情单列表为:{}", JSONObject.toJSONString(combinationDetailList));
				// 填充详情单
				fillDetailList(outStorageList, combinationDetailList);
			}
		}
		return outStorageList;
	}

	/**
	 *@Title: combinationDetailList
	 *@Description: 组合出库单详情,一个产品有两条详情,组合为一个
	 *@return List<OutStorageDetailData>
	 *@author youjun
	 *@date 2017年9月12日 下午7:19:13
	 *@param detailList
	 *@return
	 */
	private List<OutStorageDetailData> combinationDetailList(final List<OutStorageDetailData> detailList) {
		if(!CollectionUtils.isEmpty(detailList)) {
			final List<OutStorageDetailData> combinationDetailList = new ArrayList<OutStorageDetailData>();
			final Set<Integer> alreadyHandleOSId = new HashSet<Integer>(); // 已经处理了的出库单ID
			for(final OutStorageDetailData first:detailList) {
				if(alreadyHandleOSId.contains(first.getOutStorageId())) {
					continue;
				}
				alreadyHandleOSId.add(first.getOutStorageId());
				OutStorageDetailData returnDetail = first; // 默认为当前的
				boolean isExist = false; // 同一个出库单是否存在
				OutStorageDetailData matchingDetail = null;
				for(final OutStorageDetailData second:detailList) {
					if(first.getOutStorageId().intValue()==second.getOutStorageId()
							&&first.getProductId().intValue()==second.getProductId().intValue()
							&&first.getDetailId().intValue()!=second.getDetailId().intValue()) {
						isExist = true;
						matchingDetail = second;
						break;
					}
				}
				if(isExist) {
					// 返回包装单位那个
					if(first.getCodeLevel().intValue()==1) {
						returnDetail = matchingDetail;
						returnDetail.setPlanBottle(first.getPlanNum());
						returnDetail.setActualBottle(first.getActualNum());
					}else{
						returnDetail.setPlanBottle(matchingDetail.getPlanNum());
						returnDetail.setActualBottle(matchingDetail.getActualNum());
					}
				}else{ // 只有一个,判断是包装级别的还是单品的
					if(first.getCodeLevel().intValue()==1) { // 单品
						returnDetail = first;
						returnDetail.setPlanNum(0);
						returnDetail.setActualNum(0);
						returnDetail.setPlanBottle(first.getPlanNum());
						returnDetail.setActualBottle(first.getActualNum());
					}else{ // 包装
						returnDetail.setPlanBottle(0);
						returnDetail.setActualBottle(0);
					}
				}
				combinationDetailList.add(returnDetail);
				matchingDetail = null; // 重新置为null
			}
			return combinationDetailList;
		}
		return null;
	}

	/**
	 *@Title: fillDetailList
	 *@Description: 填充出库单中的详情信息
	 *@return List<OutStorageData>
	 *@author youjun
	 *@date 2017年6月25日 下午3:52:06
	 *@param dataList
	 *@param detailList
	 *@return
	 */
	private List<OutStorageData> fillDetailList(final List<OutStorageData> dataList, final List<OutStorageDetailData> detailList) {

		if(!CollectionUtils.isEmpty(dataList)
				&& !CollectionUtils.isEmpty(detailList)) {
			List<OutStorageDetailData> detailDataList = null;
			for(final OutStorageData outStorage:dataList) {
				detailDataList = new ArrayList<OutStorageDetailData>();
				int index = 1;
				for(final OutStorageDetailData detail:detailList) {
					if(detail.getOutStorageId()!=null
							&&detail.getOutStorageId().intValue()==outStorage.getOutStorageId().intValue()) {
						detailDataList.add(detail);
						if(index==1) { // 因为规则是:同一个出库单的下的产品的包装规格必须完全一致,只执行一次即可,从第一个匹配的详情中获取即可
							// 处理出库单的包装单位名称和单品单位名称,组装
							hanleOutStorageProductUnit(outStorage, detail);
						}
						index++; // 只为hanleInStorageProductUnit方法的逻辑,故在此+1
					}
				}
				outStorage.setDetailList(detailDataList);
			}
		}
		return dataList;
	}

	/**
	 *@Title: hanleOutStorageProductUnit
	 *@Description: 处理出库单总的计划、实际入库数量(带单位)
	 *@return OutStorageData
	 *@author youjun
	 *@date 2017年7月14日 下午2:55:45
	 *@param outStorage
	 *@param detail
	 */
	private OutStorageData hanleOutStorageProductUnit(final OutStorageData outStorage, final OutStorageDetailData detail) {
		try{
			if(outStorage!=null
					&&detail!=null) {
				LOGGER.info("处理出库单的总的计划、实际出库数量（带单位）");
				String productUnitName = "未知包装单位"; // 产品的包装单位名称
				String productBottleUnitName = "未知单品单位"; // 产品的单品单位名称
				final short productUnit = detail.getProductUnit()!=null?detail.getProductUnit():-1;
				final List<PackStandardData> packList = detail.getPackStandard();
				if(!CollectionUtils.isEmpty(packList)) {
					for(final PackStandardData pack:packList) {
						if(pack.getLevel()!=null) {
							if(pack.getLevel()==productUnit) {
								productUnitName = pack.getName();
							}
							if(pack.getLevel()==1) {
								productBottleUnitName = pack.getName();
							}
						}
					}
				}
				final StringBuilder planAllNum = new StringBuilder();
				final StringBuilder actualAllNum = new StringBuilder();
				planAllNum.append(outStorage.getPlanNumber()).append(productUnitName)
					.append(outStorage.getPlanSpNum()).append(productBottleUnitName);
				actualAllNum.append(outStorage.getActualNumber()).append(productUnitName)
					.append(outStorage.getActualSpNum()).append(productBottleUnitName);
				outStorage.setPlanAllNum(planAllNum.toString());
				outStorage.setActualAllNum(actualAllNum.toString());
				return outStorage;
			}
			LOGGER.info("无法处理出库单的总的计划、实际出库数量（带单位）,出库单或出库详情单为null");
		}catch(Exception e) {
			LOGGER.info("处理出库单的总的计划、实际出库数量（带单位）失败", e);
		}
		return outStorage;
	}

	/**(non-Javadoc)
	 *@Title: getChannelTypeDesc
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.OutStorageService#getChannelTypeDesc(com.sigmatrix.sc.handheld.inter.web.vo.OutStorageVo)
	 *@author youjun
	 *@date 2017年7月6日 上午11:21:49
	 */
	@Override
	public String getChannelTypeDesc(final OutStorageVo outStorageVo) {
		
		String channelTypeDesc = "未知出库类型";
		if(outStorageVo!=null) {
			final Short channelType = outStorageVo.getChannelType();
			if(channelType!=null) {
				switch (channelType) {
				case Cons.OUT_STORAGE_CHANNEL_TYPE_SALES:
					channelTypeDesc = "销售出库";
					break;
				case Cons.OUT_STORAGE_CHANNEL_TYPE_REWORK:
					channelTypeDesc = "返工出库";
					break;
				case Cons.OUT_STORAGE_CHANNEL_TYPE_TRANSFERS:
					channelTypeDesc = "调拨出库";
					break;
				case Cons.OUT_STORAGE_CHANNEL_TYPE_OTHER:
					channelTypeDesc = "其他出库";
					break;
				default:
					break;
				}
				
			}
		}
		return channelTypeDesc;
	}

	/**(non-Javadoc)
	 * @throws ServiceException 
	 *@Title: save
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.OutStorageService#save(com.sigmatrix.sc.handheld.inter.web.vo.OutStorageVo)
	 *@author youjun
	 *@date 2017年7月6日 上午11:31:51
	 */
	@Transactional(rollbackFor=ServiceException.class)
	@Override
	public Response save(final OutStorageVo outStorageVo) throws ServiceException {

		final Response validateParamsResponse = validateParamsForSave(outStorageVo);
		if(validateParamsResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
			return validateParamsResponse;
		}
		int result = 0; // 执行save操作受影响的行数
		try{
			final String enterpriseCode = outStorageVo.getEnterpriseCode();
			final OutStorage outStorage = new OutStorage();
			BeanUtils.copyProperties(outStorageVo, outStorage);
			// 先行验证orderCode不能重复
			final OutStorage outStorageCondition = new OutStorage();
			outStorageCondition.setOrderCode(outStorage.getOrderCode());
			if(validateIsExsit(outStorageCondition, enterpriseCode)) {
				return new Response(Cons.RESPONSE_CODE_EXISTED_DATA, "单据已存在");
			}
	
			//销售给门店处理
			if (outStorageVo.getCustomerType() == Cons.TAG_LEVEL_STORE) {
				outStorage.setDealerId(outStorageVo.getDealerId());//添加门店id
				outStorage.setSendDealerId(outStorageVo.getUserDealerId());//发货经销商
				//如果发货仓库为空，给与添加一个默认仓库
				if (Utils.isEmptyZero(outStorageVo.getWarehouseId())) {
					//根据发货经销商查询仓库
					final List<WarehouseData> warehouseList = 
							warehouseDao.findPartByDealerIdDefault(enterpriseCode, outStorageVo.getUserDealerId());
					if (Utils.isEmpty(warehouseList)) {
						return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "发货经销商未绑定默认仓库");
					}
					WarehouseData warehouseData = warehouseList.get(0);
					outStorageVo.setWarehouseId(warehouseData.getWarehouseId());
					outStorage.setWarehouseId(warehouseData.getWarehouseId());
				}
				else {
					outStorage.setWarehouseId(outStorageVo.getWarehouseId());
				}
			}
			
			//销售给经销商处理
			else {
				if (Utils.isNotEmptyZero(outStorageVo.getDealerId())) {
					outStorage.setDealerId(outStorageVo.getDealerId());//经销商
					outStorage.setTargetChannelId(outStorageVo.getDelegatedDealerId());//收货经销商
					outStorage.setSendDealerId(outStorageVo.getUserDealerId());//发货经销商
					//如果发货仓库为空，给与添加一个默认仓库
					if (Utils.isEmptyZero(outStorageVo.getWarehouseId())) {
						//根据发货经销商查询仓库
						final List<WarehouseData> warehouseList = 
								warehouseDao.findPartByDealerIdDefault(enterpriseCode, outStorageVo.getUserDealerId());
						if (Utils.isEmpty(warehouseList)) {
							return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "发货经销商未绑定默认仓库");
						}
						WarehouseData warehouseData = warehouseList.get(0);
						outStorageVo.setWarehouseId(warehouseData.getWarehouseId());
						outStorage.setWarehouseId(warehouseData.getWarehouseId());
					}
					else {
						outStorage.setWarehouseId(outStorageVo.getWarehouseId());
					}
					//如果仓库为空，给与添加一个默认仓库
					if (Utils.isEmptyZero(outStorageVo.getEntryWarehouseId())) {
						if (Utils.isNotEmptyZero(outStorageVo.getDelegatedDealerId())) {
							//根据经销商查询仓库
							final List<WarehouseData> warehouseList = 
									warehouseDao.findPartByDealerIdDefault(enterpriseCode, outStorageVo.getDelegatedDealerId());
							if (Utils.isEmpty(warehouseList)) {
								return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "收货经销商未绑定默认仓库");
							}
							WarehouseData warehouseData = warehouseList.get(0);
							outStorageVo.setEntryWarehouseId(warehouseData.getWarehouseId());
							outStorage.setToWarehouseId(warehouseData.getWarehouseId());
						}
						else {
							//根据经销商查询仓库
							final List<WarehouseData> warehouseList = 
									warehouseDao.findPartByDealerIdDefault(enterpriseCode, outStorageVo.getDealerId());
							if (Utils.isEmpty(warehouseList)) {
								return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "经销商未绑定默认仓库");
							}
							WarehouseData warehouseData = warehouseList.get(0);
							outStorageVo.setEntryWarehouseId(warehouseData.getWarehouseId());
							outStorage.setToWarehouseId(warehouseData.getWarehouseId());
						}
						
					}
					else {
						outStorage.setToWarehouseId(outStorageVo.getEntryWarehouseId());
					}

			}


			//调拨和其他出库   dealerId、delegatedDealerId 不传，可以去取userDealerId给赋值，但销售出库单创建的时候会传dealerId   
			//调拨出库是当前登录经销商作为发货经销商，给自己的一个仓库转移到另外一个仓库
			if (Utils.isEmptyZero(outStorageVo.getDealerId())) {
				outStorage.setDealerId(outStorageVo.getUserDealerId());
				outStorage.setSendDealerId(outStorageVo.getUserDealerId());
				//如果传递发货仓库为空，给与添加一个默认仓库 
				if (Utils.isEmptyZero(outStorageVo.getWarehouseId())) {
					//根据发货经销商查询仓库
					final List<WarehouseData> warehouseList = 
							warehouseDao.findPartByDealerIdDefault(enterpriseCode, outStorageVo.getUserDealerId());
					if (Utils.isEmpty(warehouseList)) {
						return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "经销商未绑定默认仓库");
					}
					WarehouseData warehouseData = warehouseList.get(0);
					outStorageVo.setWarehouseId(warehouseData.getWarehouseId());
					outStorage.setWarehouseId(warehouseData.getWarehouseId());
				}
				else {
					outStorage.setWarehouseId(outStorageVo.getWarehouseId());
				}
				//调拨出库
				if (outStorageVo.getChannelType() == Cons.OUT_STORAGE_CHANNEL_TYPE_TRANSFERS) {
					//入库仓库校验  入库仓库必填
					if (Utils.isEmptyZero(outStorageVo.getEntryWarehouseId())) {
						return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "入库仓库不能为空");
					}
					//如果是调拨出库 此仓库就是入库仓库
					outStorage.setToWarehouseId(outStorageVo.getEntryWarehouseId());
					
					//调拨出库中 入库仓库跟出库仓库不能相同
					if (outStorage.getWarehouseId() == outStorage.getToWarehouseId()) {
						return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "出入库仓库不能一致");
					}
				}
				
			
			}
			
			}
		
			
			if(outStorage.getStatus()==null) {
				outStorage.setStatus(OutStorageStatusEnum.NO_AUDIT.getId().shortValue());
				// 目前无审核操作,自动审核
				audit(outStorage);
			}
			outStorage.setCreateTime(new Date());
			outStorage.setOutStorageType(Cons.OUT_STORAGE_TYPE_PDA);
			fillOutStorage(outStorageVo, outStorage);
			final long outStorageId = outStorageDao.save(outStorage, enterpriseCode);
			// 保存出库单详情
			if(outStorageId>0) {
				outStorage.setId(outStorageId);
				final List<OutStorageDetailVo> detailVoList = outStorageVo.getDetailList();
				final List<OutStorageDetail> detailList = filterAndFillOutStorageDetailList(detailVoList, outStorage, enterpriseCode);
				if(CollectionUtils.isEmpty(detailList)) {
					throw new ServiceException("保存出库单时出库单详情必须存在");
				}
				result = outStorageDetailDao.batchSave(detailList, enterpriseCode);
			}else{
				LOGGER.error("保存出库单失败");
			}
			//保存出库单绑定用户
			OutStorageTerminalUser outStorageTerminalUser = new OutStorageTerminalUser();
			outStorageTerminalUser.setOutStorageId(Integer.valueOf(String.valueOf(outStorageId)));
			outStorageTerminalUser.setTerminalUserId(outStorageVo.getUserId());
			outStorageTerminalUserDao.save(outStorageTerminalUser, enterpriseCode);
			
			if(result > 0) {
				return findById(outStorageVo, outStorageId);
			}
			
			return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "执行操作失败");
		}catch(Exception e) {
			throw new ServiceException(e);
		}
	}

	/**
	 *@Title: audit
	 *@Description: 审核
	 *@return void
	 *@author youjun
	 *@date 2017年8月10日 下午8:35:58
	 *@param outStorage
	 */
	private void audit(final OutStorage outStorage) {

		if(outStorage!=null) {
			outStorage.setStatus(OutStorageStatusEnum.HAS_AUDIT.getId().shortValue());
		}
	}

	/**
	 *@Title: fillOutStorage
	 *@Description: 填充出库单
	 *@return OutStorage
	 *@author youjun
	 *@date 2017年7月11日 下午8:57:34
	 *@param outStorageVo
	 *@param outStorage
	 */
	private OutStorage fillOutStorage(final OutStorageVo outStorageVo, final OutStorage outStorage) {

		if(outStorageVo!=null||outStorage!=null) {
			final List<OutStorageDetailVo> detailList = outStorageVo.getDetailList();
			if(!CollectionUtils.isEmpty(detailList)) {
				int planNum = 0;
				int planBottle = 0;
				for(final OutStorageDetailVo detailVo:detailList) {
					planNum+=detailVo.getPlanNum()!=null?detailVo.getPlanNum():0;
					planBottle+=detailVo.getPlanBottle()!=null?detailVo.getPlanBottle():0;
				}
				outStorage.setPlanNumber(planNum);
				outStorage.setPlanSpNum(planBottle);
			}
		}
		return outStorage;
	}

	/**
	 *@Title: validateIsExsit
	 *@Description: 验证是否存在
	 *@return boolean
	 *@author youjun
	 *@date 2017年7月8日 下午4:21:50
	 *@param outStorageCondition
	 *@param enterpriseCode
	 *@return
	 * @throws ServiceException 
	 */
	private boolean validateIsExsit(final OutStorage outStorageCondition, final String enterpriseCode) throws ServiceException {

		try {
			final List<OutStorageData> list = outStorageDao.findPartColumnListByCondition(outStorageCondition, enterpriseCode);
			if(!CollectionUtils.isEmpty(list)) {
				return true;
			}
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
		return false;
	}

	/**
	 *@Title: filterAndFillOutStorageDetailList
	 *@Description: 过滤错误的出库单详情并填充出库单详情中的出库单ID、出库单号等信息
	 *@return List<OutStorageDetail>
	 *@author youjun
	 *@date 2017年7月6日 下午1:38:37
	 *@param detailVoList
	 * @param enterpriseCode 
	 *@return
	 * @throws ServiceException 
	 */
	private List<OutStorageDetail> filterAndFillOutStorageDetailList(final List<OutStorageDetailVo> detailVoList, 
			final OutStorage outStorage, final String enterpriseCode) throws ServiceException {
		
		if(outStorage==null||outStorage.getId()==null||StringUtils.isBlank(outStorage.getOrderCode())) {
			throw new ServiceException("出库单信息不能为空");
		}
		if(!CollectionUtils.isEmpty(detailVoList)) {
			final List<OutStorageDetail> detailList = new ArrayList<OutStorageDetail>();
			OutStorageDetail packDetail = null; // 包装数量详情
			OutStorageDetail singleDetail = null; // 单品数量详情
			ProductData product = null; // 产品信息
			for(final OutStorageDetailVo vo:detailVoList) {
				product = productService.findById(vo.getProductId(), true, enterpriseCode);
				final Map<Integer, String> packStandardMap = product.getPackStandardMap();
				final int packLevel = product.getProductUnit().intValue(); // 包装级别
				// 包装详情
				packDetail = new OutStorageDetail();
				BeanUtils.copyProperties(vo, packDetail);
				packDetail.setOutStorageId(outStorage.getId());
				packDetail.setCodeLevel(packLevel);
				packDetail.setWrapName(packStandardMap.get(packLevel));
				packDetail.setPlanSpNum(null);
				packDetail.setCreateTime(new Date());
				packDetail.setProductCode(product.getProductCode());
				// 状态默认为未审核
				packDetail.setStatus(OutStorageStatusEnum.NO_AUDIT.getId().shortValue());
				// 自动转为已审核
				auditDetail(packDetail);
				packDetail.setOrderCode(outStorage.getOrderCode());
				detailList.add(packDetail);
				// 单品详情
				singleDetail = new OutStorageDetail();
				BeanUtils.copyProperties(vo, singleDetail);
				singleDetail.setPlanNum(vo.getPlanBottle());
				singleDetail.setOutStorageId(outStorage.getId());
				singleDetail.setCodeLevel(1);
				singleDetail.setWrapName(packStandardMap.get(1));
				singleDetail.setPlanSpNum(null);
				singleDetail.setCreateTime(new Date());
				singleDetail.setProductCode(product.getProductCode());
				// 状态默认为未审核
				singleDetail.setStatus(OutStorageStatusEnum.NO_AUDIT.getId().shortValue());
				// 自动转为已审核
				auditDetail(singleDetail);
				singleDetail.setOrderCode(outStorage.getOrderCode());
				detailList.add(singleDetail);
			}
			return detailList;
		}
		return null;
	}

	/**
	 *@Title: auditDetail
	 *@Description: TODO()
	 *@return void
	 *@author youjun
	 *@date 2017年8月15日 下午1:40:22
	 *@param detail
	 */
	private void auditDetail(OutStorageDetail detail) {
		if(detail!=null) {
			if(detail.getStatus().intValue()==OutStorageStatusEnum.NO_AUDIT.getId()){
				detail.setStatus(OutStorageStatusEnum.HAS_AUDIT.getId().shortValue());
			}
		}
	}

	/**
	 *@Title: validateParamsForSave
	 *@Description: 保存出库单时验证参数
	 *@return Response
	 *@author youjun
	 *@date 2017年7月6日 上午11:35:20
	 *@param outStorageVo
	 *@return
	 */
	private Response validateParamsForSave(final OutStorageVo outStorageVo) {

		Response validateResponse = validateBasicParams(outStorageVo);
		if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
			return validateResponse;
		}
		Short channelType = outStorageVo.getChannelType();
		if(channelType==null) {
			return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "出库类型不能为空");
		}
		if(StringUtils.isBlank(outStorageVo.getOrderCode())) {
			return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "出库单号不能为空");
		}
		/*if(outStorageVo.getWarehouseId()==null) {
			return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "出库仓库不能为空");
		}*/
		if(channelType==Cons.OUT_STORAGE_CHANNEL_TYPE_SALES) { // 销售出库:经销商必填
			if(outStorageVo.getDealerId()==null) {
				return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "经销商不能为空");
			}
		}else if(channelType==Cons.OUT_STORAGE_CHANNEL_TYPE_REWORK) { // 返工出库，经销商入库无
			
		}else if(channelType==Cons.OUT_STORAGE_CHANNEL_TYPE_TRANSFERS) { // 调拨出库:入库仓库必填
			/*if(outStorageVo.getToWarehouseId()==null) {
				return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "目标仓库不能为空");
			}*/
		}else if(channelType==Cons.OUT_STORAGE_CHANNEL_TYPE_OTHER) { // 其他出库
			
		}
		if(CollectionUtils.isEmpty(outStorageVo.getDetailList())) {
			return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "出库单详情不能为空");
		}
		return new Response(ResponseEnum.SUCCESS);
	}

	/**(non-Javadoc)
	 *@Title: findById
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.OutStorageService#findById(com.sigmatrix.sc.handheld.inter.request.Request, java.lang.Long)
	 *@author youjun
	 *@date 2017年7月8日 下午3:04:00
	 */
	@Override
	public Response findById(final Request baseRequest, final Long outStorageId) throws ServiceException {

		final Response validateResponse = validateBasicParams(baseRequest);
		if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
			return validateResponse;
		}
		if(outStorageId==null) {
			return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "出库单ID不能为空");
		}
		try {
			final String enterpriseCode = baseRequest.getEnterpriseCode();
			final OutStorage outStorage = outStorageDao.findById(outStorageId, enterpriseCode);
			if(outStorage==null) {
				return new Response(Cons.RESPONSE_CODE_NO_DATA, "该出库单不存在");
			}
			final OutStorageData outStorageData = new OutStorageData();
			BeanUtils.copyProperties(outStorage, outStorageData);
			outStorageData.setDelegatedDealerId(outStorage.getTargetChannelId()); //目标经销商
			outStorageData.setEntryWarehouseId(outStorage.getToWarehouseId());//目标仓库
			outStorageData.setOutStorageId(outStorage.getId());
			final Date createTime = outStorage.getCreateTime();
			if(createTime!=null) {
				outStorageData.setCreateTime(DateUtil.getDateTimeStr(createTime));
			}
			
			final List<OutStorageData> outStorageList = new ArrayList<OutStorageData>();
			outStorageList.add(outStorageData);
			final List<OutStorageData> resultList = fillDataList(outStorageList, enterpriseCode, true);
			if(CollectionUtils.isEmpty(resultList)) { // 前面已经判断为空,运行到这里仍然为null,报错
				throw new ServiceException("找到出库单后处理完后却变为空了");
			}
			return new Response(ResponseEnum.SUCCESS, resultList.get(0));
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}

	/**(non-Javadoc)
	 *@Title: update
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.OutStorageService#update(com.sigmatrix.sc.handheld.inter.web.vo.OutStorageVo)
	 *@author youjun
	 *@date 2017年8月3日 下午10:20:17
	 */
	@Transactional(rollbackFor=ServiceException.class)
	@Override
	public Response update(final OutStorageVo outStorageVo) throws ServiceException {

		final Map<String, Object> resultMap = new HashMap<String, Object>();
		try{
			// 第一步:出库操作验证
			final Map<String, Object> map = validateUpdate(outStorageVo);
			Response validateResponse = (Response) map.get("validateResponse");
			if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
				return validateResponse;
			}
			// 第二步:更新出库单与出库单详情,包括状态、实际数量、操作码、成功码
			LOGGER.info("更新出库单和明细的码内容和实际数量----------开始");
			updateOutStorageInfo(outStorageVo, map);
			LOGGER.info("更新出库单和明细的码内容和实际数量----------结束");
			
			resultMap.put("outStorageStatus", map.get("outStorageStatus"));
			// 第三步:更新库存
			LOGGER.info("更新库存的实际数量，库存明细数量-------------开始");
			updateWarehouseStock(outStorageVo, map);
			LOGGER.info("更新库存的实际数量，库存明细数量-------------结束");
			
			// 若是调拨出库,入库仓库增库存,不创建入库单
			if (outStorageVo.getChannelType() == Cons.OUT_STORAGE_CHANNEL_TYPE_TRANSFERS) {
				LOGGER.info("调拨出库,更改调拨进入的仓库的库存开始...");
				addWarehouseStock(outStorageVo, map);
				LOGGER.info("调拨出库,更改调拨进入的仓库的库存结束...");
			}
			
//			// 第四步（放到最后，待以上数据库操作成功后再做）:mongo操作:1、记录码流转记录(实体码的)2、改为出库状态,解除虚拟码与包装码的关系
			try { // mongo操作即使失败也不进行回滚
				LOGGER.info("出库单ID为:{}的出库单对mongo中存储的码进行操作start...", outStorageVo.getOutStorageId());
				ThreadPool.executor.submit(() -> {
					try {
						codeCirculationService.saveOutStorageCodeCirculation(outStorageVo, map); // 码流转记录
					} catch (Exception e) {
						LOGGER.error("出库单ID为:{}的出库单对mongo中的码进行操作失败", outStorageVo.getOutStorageId(), e);
						syncTaskService.updateSyncErrorMsg(outStorageVo.getEnterpriseCode(), outStorageVo.getSyncTaskId(), 
								"出库单ID为:"+outStorageVo.getOutStorageId()+"的出库单对mongo中的码进行操作失败");
					}
				});	
				// 分销出库不更新码，反映在码的流转记录上
//				multistageLevelQRCodeService.updateOutStorageCode(outStorageVo, map);
				LOGGER.info("出库单ID为:{}的出库单对mongo中存储的码进行操作成功end...", outStorageVo.getOutStorageId());
			} catch (Exception e) {
				LOGGER.error("出库单ID为:{}的出库单对mongo中的码进行操作失败", outStorageVo.getOutStorageId(), e);
			}
			try {
				
				//下级经销商做自动收货入库   只有销售出库才会做收货入库(其它出库类型不会调用)   并且只有销售给经销商
			if (outStorageVo.getChannelType() == Cons.OUT_STORAGE_CHANNEL_TYPE_SALES && outStorageVo.getCustomerType() == Cons.TAG_LEVEL_DEALER) {
				//自动收货入库操作做异步处理
				ThreadPool.executor.submit(() -> {
					try {
						//获取自动收货入库信息
						InStorageAutoAddVo inStorageAutoAddVo = inStorageAutoAddVoSwitch(outStorageVo);
						LOGGER.info("获取转化后的InStorageAutoAddVo信息...{}", JSONObject.toJSONString(inStorageAutoAddVo));
						//新增入库单
						//获取转换InStorageAddVo
						InStorageAddVo inStorageVo = this.inStorageAddSwitch(inStorageAutoAddVo);
						LOGGER.info("获取转化后的新增收获入库单InStorageAddVo信息...{}", JSONObject.toJSONString(inStorageVo));
						//新增入库单 (判断入库单是否存在)
						Response addResponse = inStorageService.saveAuto(inStorageVo);
						LOGGER.info("新增收货入库单返回信息...{}", JSONObject.toJSONString(addResponse));
						//获取转换inStorageAutoAddVo
						InStorageUpLoadVo inStorageUpLoadVo = this.inStorageUpLoadVoSwitch(inStorageAutoAddVo);
						LOGGER.info("获取转化后的收货入库单上传参数InStorageUpLoadVo信息...{}", JSONObject.toJSONString(inStorageUpLoadVo));
						//收获入库上传
						Response upLoadResponse = inStorageService.updateAuto(inStorageUpLoadVo);
						LOGGER.info("上传收货入库单返回信息...{}", JSONObject.toJSONString(upLoadResponse));
					} catch (Exception e) {
						LOGGER.error("自动收货入库操作失败", e);
					}
				});	
				
				}

				// 仅仅是最后返回结果时出错，不进行回滚
				return findById(outStorageVo, outStorageVo.getOutStorageId());
			} catch (ServiceException e) {
				LOGGER.error("操作成功，同步数据失败，请手动同步出库单", e);
				return new Response(Cons.RESPONSE_CODE_EXCEPTION, "操作成功，同步数据失败，请手动同步出库单");
			}
		}catch(Exception e){
			LOGGER.error("出库上传操作失败", e);
			throw new ServiceException(e);
		}
//		return new Response(ResponseEnum.SUCCESS, resultMap);
	}

	
	private InStorageUpLoadVo inStorageUpLoadVoSwitch(InStorageAutoAddVo inStorageAutoAddVo) throws DaoException {
		InStorageUpLoadVo inStorageUpLoadVo = new InStorageUpLoadVo();
		InStorage inStorage = inStorageDao.findByOrderCode(inStorageAutoAddVo.getOrderCode(), inStorageAutoAddVo.getEnterpriseCode());
		inStorageUpLoadVo.setWarehouseId(inStorage.getWarehouseId());
		//inStorageUpLoadVo.setUserId(inStorage.getCreatorId());//自动入库没有上传用户id
		inStorageUpLoadVo.setDealerId(inStorage.getDealerId());
		inStorageUpLoadVo.setChannelType(inStorage.getChannelType());
		inStorageUpLoadVo.setInStorageId(inStorage.getId());
		inStorageUpLoadVo.setEnterpriseCode(inStorageAutoAddVo.getEnterpriseCode());
		inStorageUpLoadVo.setDealerId(inStorage.getDealerId());
		inStorageUpLoadVo.setUserDealerId(inStorage.getDealerId());
		inStorageUpLoadVo.setStatus(inStorageAutoAddVo.getStatus());
		
		List<InStorageUpLoadDetailVo> detailList = new ArrayList<>();
		
		List<InStorageUpLoadAutoDetailVo> upLoadDetailList = inStorageAutoAddVo.getUpLoadDetailList();
		
		for (InStorageUpLoadAutoDetailVo vo : upLoadDetailList) {
			InStorageUpLoadDetailVo inStorageUpLoadDetailVo = new InStorageUpLoadDetailVo();
			Product product = productDao.findByCode(vo.getProductCode(), inStorageAutoAddVo.getEnterpriseCode());
			inStorageUpLoadDetailVo.setProductId(product.getId());
			List<OperateCodeUpLoadVo> operateCodeList =  new ArrayList<>();
			
			List<OperateCodeUpLoadVo> operateCodeAutoList = vo.getOperateCodeList();
			for (OperateCodeUpLoadVo autoVo : operateCodeAutoList) {
				OperateCodeUpLoadVo operateCodeUpLoadVo = new OperateCodeUpLoadVo();
				BeanUtils.copyProperties(autoVo, operateCodeUpLoadVo);
				operateCodeList.add(operateCodeUpLoadVo);
			}
			
			inStorageUpLoadDetailVo.setOperateCodeList(operateCodeList);
			detailList.add(inStorageUpLoadDetailVo);
		}
		inStorageUpLoadVo.setDetailList(detailList);
		return inStorageUpLoadVo;
	}
	
	/**
	 * 
	 *@Title: InStorageAddSwitch
	 *@Description: 获取转换InStorageAddVo
	 *@return InStorageAddVo
	 *@author gaomingkai
	 *@date 2018年1月19日 下午2:47:24
	 *@param inStorageAutoAddVo
	 *@return
	 */
	private InStorageAddVo inStorageAddSwitch(InStorageAutoAddVo inStorageAutoAddVo) {
		InStorageAddVo inStorageVo = new InStorageAddVo();
		inStorageVo.setEnterpriseCode(inStorageAutoAddVo.getEnterpriseCode());
		inStorageVo.setUserId(inStorageAutoAddVo.getUserId());
		inStorageVo.setUserDealerId(inStorageAutoAddVo.getUserDealerId());
		inStorageVo.setChannelType(inStorageAutoAddVo.getChannelType());
		inStorageVo.setOrderCode(inStorageAutoAddVo.getOrderCode());
		inStorageVo.setWarehouseId(inStorageAutoAddVo.getWarehouseId());
		inStorageVo.setSource(inStorageAutoAddVo.getSource());
		inStorageVo.setRemarks(inStorageAutoAddVo.getRemarks());
		inStorageVo.setInStorageType(inStorageAutoAddVo.getInStorageType());
		inStorageVo.setStatus(inStorageAutoAddVo.getStatus());
		
		List<InStorageDetailAddVo> detailList = new ArrayList<>();
		
		List<InStorageDetailAutoAddVo> detailListAutos = inStorageAutoAddVo.getDetailList();
		for (InStorageDetailAutoAddVo detailAutoAddVo : detailListAutos) {
			InStorageDetailAddVo inStorageDetailAddVo = new InStorageDetailAddVo();
			Product product = null;
			try {
				product = productDao.findByCode(detailAutoAddVo.getProductCode(), inStorageAutoAddVo.getEnterpriseCode());
			} catch (DaoException e) {
			}
			inStorageDetailAddVo.setProductId(product.getId());
			inStorageDetailAddVo.setPlanNum(detailAutoAddVo.getPlanNum());
			inStorageDetailAddVo.setPlanSpNum(detailAutoAddVo.getPlanSpNum());
			detailList.add(inStorageDetailAddVo);
		}
		inStorageVo.setDetailList(detailList);
		
		return inStorageVo;
	}
	
	/**
	 * 
	 *@Title: inStorageAutoAddVoSwitch
	 *@Description: TODO()
	 *@return InStorageAutoAddVo
	 *@author gaomingkai
	 *@date 2018年1月22日 上午10:58:35
	 *@param outStorageVo
	 *@return
	 *@throws DaoException
	 *@throws ServiceException
	 */
	private InStorageAutoAddVo inStorageAutoAddVoSwitch(final OutStorageVo outStorageVo) throws DaoException, ServiceException {
		LOGGER.info("outStorageVo对象进行转化信息...{}", JSONObject.toJSONString(outStorageVo));
		//出库单
		OutStorage outStorage = outStorageDao.findById(outStorageVo.getOutStorageId(), outStorageVo.getEnterpriseCode());
		//收货入库上传数据
		InStorageAutoAddVo inStorageAutoAddVo = new InStorageAutoAddVo();
		inStorageAutoAddVo.setEnterpriseCode(outStorageVo.getEnterpriseCode());
		inStorageAutoAddVo.setUserId(outStorage.getCreatorId());
		if (Utils.isEmptyZero(outStorage.getTargetChannelId())) {
			inStorageAutoAddVo.setUserDealerId(outStorage.getDealerId());
		}
		else {
			inStorageAutoAddVo.setUserDealerId(outStorage.getTargetChannelId());
		}
		inStorageAutoAddVo.setOrderCode(Cons.RECEIPT_SINGLE_NUMBER_PREFIX+outStorage.getOrderCode());
		inStorageAutoAddVo.setWarehouseId(outStorage.getToWarehouseId());
		inStorageAutoAddVo.setSource("");
		inStorageAutoAddVo.setRemarks("");
		inStorageAutoAddVo.setPlanNum(outStorage.getPlanNumber());
		inStorageAutoAddVo.setPlanSpNum(outStorage.getPlanSpNum());
		inStorageAutoAddVo.setStatus(outStorage.getStatus());
		inStorageAutoAddVo.setChannelType(Cons.IN_CHANNEL_TYPE_REC);
		inStorageAutoAddVo.setInStorageType(Cons.IN_STORAGE_TYPE_PLAT );
		
		List<InStorageDetailAutoAddVo> detailList = new ArrayList<>();
		
		OutStorageDetail outStorageDetail = new OutStorageDetail();
		outStorageDetail.setOutStorageId(outStorage.getId());
		List<OutStorageDetail> OutStorageDetailList = outStorageDetailDao.findListByCondition(outStorageDetail, outStorageVo.getEnterpriseCode());
		for (OutStorageDetail detail : OutStorageDetailList) {
			InStorageDetailAutoAddVo inStorageDetailAutoAddVo = new InStorageDetailAutoAddVo();
			inStorageDetailAutoAddVo.setPlanNum(detail.getPlanNum());
			inStorageDetailAutoAddVo.setPlanSpNum(detail.getPlanSpNum());
			if (Utils.isEmpty(detail.getProductCode())) {
				Product product = productDao.findById(detail.getProductId(), outStorageVo.getEnterpriseCode());
				inStorageDetailAutoAddVo.setProductCode(product.getCode());
			}
			else {
				inStorageDetailAutoAddVo.setProductCode(detail.getProductCode());
			}
			detailList.add(inStorageDetailAutoAddVo);
		}
		
		//添加入库单明细信息
		inStorageAutoAddVo.setDetailList(detailList);
		
		
		
		List<InStorageUpLoadAutoDetailVo> autoUpLoadDetailList = new ArrayList<>();
		List<OutStorageDetailVo> upLoadDetailList = outStorageVo.getDetailList();
		for (OutStorageDetailVo outStorageDetailVo : upLoadDetailList) {
			InStorageUpLoadAutoDetailVo inStorageUpLoadAutoDetailVo = new InStorageUpLoadAutoDetailVo();
			ProductData productData = productService.findById(outStorageDetailVo.getProductId(), outStorageVo.getEnterpriseCode());
			//添加产品code
			inStorageUpLoadAutoDetailVo.setProductCode(productData.getProductCode());
			List<OperateCodeUpLoadVo> autoOperateCodeList = new ArrayList<>();
			List<OperateCodeVo> operateCodeList = outStorageDetailVo.getOperateCodeList();
			for (OperateCodeVo operateCodeVo : operateCodeList) {
				OperateCodeUpLoadVo operateCodeUpLoadVo = new OperateCodeUpLoadVo();
				operateCodeUpLoadVo.setCode(operateCodeVo.getCode());
				operateCodeUpLoadVo.setLevel(operateCodeVo.getLevel());
				autoOperateCodeList.add(operateCodeUpLoadVo);
			}

			inStorageUpLoadAutoDetailVo.setOperateCodeList(autoOperateCodeList);
			//添加码详情
			autoUpLoadDetailList.add(inStorageUpLoadAutoDetailVo);
			
		}
		//添加码详情
		inStorageAutoAddVo.setUpLoadDetailList(autoUpLoadDetailList);
		
		return inStorageAutoAddVo;
	}

	/**
	 *@Title: updateWarehouseStock
	 *@Description: 更新库存信息
	 *@return void
	 *@author youjun
	 *@date 2017年8月8日 上午1:07:57
	 *@param outStorageVo
	 *@param map
	 */
	@Transactional(rollbackFor=ServiceException.class)
	public Response updateWarehouseStock(final OutStorageVo outStorageVo, final Map<String, Object> map) throws ServiceException {
		
		if(outStorageVo.getOutStorageId()==null
				||outStorageVo.getWarehouseId()==null
				||StringUtils.isBlank(outStorageVo.getEnterpriseCode())
				||CollectionUtils.isEmpty(map)
				||map.get("currentActualNumMap")==null) {
			throw new ServiceException("参数有误");
		}
		try{
			final String enterpriseCode = outStorageVo.getEnterpriseCode();
			final int warehouseId = outStorageVo.getWarehouseId();
			@SuppressWarnings("unchecked")
			final Map<Integer, Integer[]> currentActualNumMap = (Map<Integer, Integer[]>) map.get("currentActualNumMap");
			// 出库的单品码和包装单位码
			@SuppressWarnings("unchecked")
			final List<MultistageLevelQRCode> outPackLevelCodeList = (List<MultistageLevelQRCode>) map.get("outPackLevelCodeList");
			@SuppressWarnings("unchecked")
			final List<FirstLevelQRCode> outSingleLevelCodeList = (List<FirstLevelQRCode>) map.get("outSingleLevelCodeList");
			for(final Integer productId:currentActualNumMap.keySet()) {
				// 根据仓库ID、产品ID、批次号、当前批次出库单品数量、包装单位数量减库存,成功返回"success"
				final String result = warehouseStockService.reduceStockNum(warehouseId, productId, currentActualNumMap.get(productId)[0], currentActualNumMap.get(productId)[1], enterpriseCode);
				if(!result.equals(Cons.SUCCESS_MESSAGE)) {
					return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, result);
				}
				// 更新库存详情的包装码状态
				// 从出库的包装码集合中按产品、批次找出对应的包装码
				final List<String> boxList = QRCodeUtil.classificationByProductId(outPackLevelCodeList, null, productId);
				final List<String> singleList = QRCodeUtil.classificationByProductId(null, outSingleLevelCodeList, productId);
				warehouseStockDetailsService.batchUpdateBoxsStatus(warehouseId, productId, boxList, singleList,Cons.WH_STOCK_DETAILS_STATUS_OUT_STORAGE, enterpriseCode);
			}
			return new Response(ResponseEnum.SUCCESS);
		}catch(Exception e){
			LOGGER.error("出库单更新库存失败", e);
			throw new ServiceException(e);
		}
	}
	
	/**
	 * 
	 *@Title: addWarehouseStock
	 *@Description: TODO()
	 *@return Response
	 *@author gaomingkai
	 *@date 2018年1月31日 上午11:23:49
	 *@param outStorageVo
	 *@param map
	 *@return
	 *@throws ServiceException
	 */
	@Transactional(rollbackFor=ServiceException.class)
	public Response addWarehouseStock(final OutStorageVo outStorageVo, final Map<String, Object> map) throws ServiceException {
		
		if(outStorageVo.getOutStorageId()==null
				||outStorageVo.getWarehouseId()==null
				||StringUtils.isBlank(outStorageVo.getEnterpriseCode())
				||CollectionUtils.isEmpty(map)
				||map.get("currentActualNumMap")==null) {
			throw new ServiceException("参数有误");
		}
		try{
			//出库单
			final OutStorage outStorage = outStorageDao.findById(outStorageVo.getOutStorageId(), outStorageVo.getEnterpriseCode());
			final String enterpriseCode = outStorageVo.getEnterpriseCode();
			//目标仓库  入库仓库
			final int  toWarehouseId = outStorage.getToWarehouseId();
			@SuppressWarnings("unchecked")
			final Map<Integer, Integer[]> currentActualNumMap = (Map<Integer, Integer[]>) map.get("currentActualNumMap");
			// 出库的单品码和包装单位码
			@SuppressWarnings("unchecked")
			final List<MultistageLevelQRCode> outPackLevelCodeList = (List<MultistageLevelQRCode>) map.get("outPackLevelCodeList");
			@SuppressWarnings("unchecked")
			final List<FirstLevelQRCode> outSingleLevelCodeList = (List<FirstLevelQRCode>) map.get("outSingleLevelCodeList");
			for(final Integer productId:currentActualNumMap.keySet()) {
				// 库存,库存明细添加,成功返回"success"(暂时不支持按盒出库)
				String result = warehouseStockService.addStockNum(toWarehouseId, productId, currentActualNumMap.get(productId)[0], currentActualNumMap.get(productId)[1], outPackLevelCodeList,outSingleLevelCodeList,outStorageVo,enterpriseCode);
				if(!result.equals(Cons.SUCCESS_MESSAGE)) {
					return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, result);
				}

			}
			return new Response(ResponseEnum.SUCCESS);
		}catch(Exception e){
			LOGGER.error("出库单更新库存失败", e);
			throw new ServiceException(e);
		}
	}


	/**
	 *@Title: updateOutStorageInfo
	 *@Description: 更新出库单信息，出库单状态将放入map中(key为outStorageStatus)
	 *@return void
	 *@author youjun
	 *@date 2017年8月7日 下午6:13:32
	 *@param outStorageVo
	 *@param map
	 */
	@Transactional(rollbackFor=ServiceException.class)
	public void updateOutStorageInfo(final OutStorageVo outStorageVo, final Map<String, Object> map) throws ServiceException {

		if(outStorageVo==null
				||outStorageVo.getOutStorageId()==null
				||CollectionUtils.isEmpty(outStorageVo.getDetailList())
				||StringUtils.isBlank(outStorageVo.getEnterpriseCode())) {
			throw new ServiceException("参数有误");
		}
		final String enterpriseCode = outStorageVo.getEnterpriseCode();
		try {
			final OutStorage outStorage = outStorageDao.findById(outStorageVo.getOutStorageId(), enterpriseCode);
			if(outStorage==null) {
				throw new ServiceException("未找到出库单");
			}
			outStorageVo.setWarehouseId(outStorage.getWarehouseId()); // 填充出库仓库
			// 1、更新出库单详情
			final OutStorageDetail outStorageDetailCondition = new OutStorageDetail();
			outStorageDetailCondition.setOutStorageId(outStorage.getId());
			final List<OutStorageDetail> outStorageDetailList = outStorageDetailDao.findListByCondition(outStorageDetailCondition, enterpriseCode);
			// 只更新传递的Vo
			final List<OutStorageDetailVo> detailVoList = outStorageVo.getDetailList();
			for(final OutStorageDetailVo detailVo:detailVoList){
				for(final OutStorageDetail detail:outStorageDetailList) {
					// 目前的原则是:在同一个出库单中产品分两条存储
					if(detailVo.getProductId().intValue()==detail.getProductId()) {
						if(detail.getCodeLevel()==1) { // 单品
							detail.setActualNum(detailVo.getActualBottle()); // 实际出库瓶
						}else{
							detail.setActualNum(detailVo.getActualNum()); // 实际出库箱
						}
						String operateCodeContent = StringUtils.isNotBlank(detail.getOperateContent())?detail.getOperateContent()+",":"";
						operateCodeContent += getOperateCodeContent(map, detail.getProductId(), detail.getCodeLevel());
						detail.setOperateContent(operateCodeContent);
						final int planNum = detail.getPlanNum()!=null?detail.getPlanNum():0;
						if(detail.getCodeLevel()==1) { // 单品
							if(planNum==detailVo.getActualBottle()){ // 计划数量和实际数量正好相等,说明已完成
								detail.setStatus(OutStorageStatusEnum.COMPLETE.getId().shortValue());
							}else{ // 否则,进行中
								detail.setStatus(OutStorageStatusEnum.PROCEED_ING.getId().shortValue());
							}
						}else{
							if(planNum==detailVo.getActualNum()){ // 计划数量和实际数量正好相等,说明已完成
								detail.setStatus(OutStorageStatusEnum.COMPLETE.getId().shortValue());
							}else{ // 否则,进行中
								detail.setStatus(OutStorageStatusEnum.PROCEED_ING.getId().shortValue());
							}
						}
						outStorageDetailDao.update(detail, enterpriseCode);
					}
				}
			}
			// 2、更新出库单主表
			if(outStorage.getOutStorageTime()==null){
				outStorage.setOutStorageTime(new Date()); // 出库时间
			}
			outStorage.setUpdateTime(new Date());
			if(outStorage.getStartTime()==null){
				outStorage.setStartTime(new Date()); // 出库开始时间,出库结束时间则在所有产品都完成出库后set
			}
			outStorage.setOutStorageType(Cons.OUT_STORAGE_TYPE_MIX);
			setOutStorageInfoByDetailInfo(outStorage, outStorageDetailList);
			map.put("outStorageStatus", outStorage.getStatus()); // 放入当前出库单的状态
			outStorageDao.update(outStorage, enterpriseCode);
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}

	/**
	 *@Title: setOutStorageInfoByDetailInfo
	 *@Description: TODO()
	 *@return void
	 *@author youjun
	 *@date 2017年8月7日 下午10:47:44
	 *@param outStorage
	 *@param outStorageDetailList
	 */
	private void setOutStorageInfoByDetailInfo(final OutStorage outStorage, final List<OutStorageDetail> outStorageDetailList) 
		throws ServiceException {
		if(outStorage==null
				||CollectionUtils.isEmpty(outStorageDetailList)) {
			throw new ServiceException("参数有误");
		}
		int totalActualBottle = 0; // 总出库瓶数
		int totalActualNum = 0; // 总出库箱数
		final Set<Integer> detailStatusSet = new HashSet<Integer>(); // 所有不重复的状态
		for(final OutStorageDetail detail:outStorageDetailList) {
			if(detail.getCodeLevel()==1) {
				totalActualBottle+=detail.getActualNum()!=null?detail.getActualNum():0;
			}else{
				totalActualNum+=detail.getActualNum()!=null?detail.getActualNum():0;
			}
			if(detail.getStatus()==null){
				throw new ServiceException("当前出库单的详情中存在状态为空的");
			}
			detailStatusSet.add(detail.getStatus().intValue());
		}
		Integer outStorageStatus = null;
		// 出库单的顶级状态,出现一个,则主表的状态即为该状态
		final List<Integer> topStatusList = new ArrayList<Integer>();
		topStatusList.add(OutStorageStatusEnum.EXCEPTION.getId());
		topStatusList.add(OutStorageStatusEnum.PROCEED_ING.getId());
		for(final Integer status:detailStatusSet){
			outStorageStatus = status;
			if(topStatusList.contains(status)) {
				outStorageStatus = status;
				break;
			}
		}
		outStorage.setStatus(outStorageStatus.shortValue());
		outStorage.setActualSpNum(totalActualBottle);
		outStorage.setActualNumber(totalActualNum);
		if(OutStorageStatusEnum.COMPLETE.getId().shortValue()==outStorage.getStatus()
				&&outStorage.getEndTime()!=null) {
			outStorage.setEndTime(new Date()); // 结束时间
		}
	}

	/**
	 *@Title: getOperateCodeContent
	 *@Description: 获得操作码内容
	 *@return String
	 *@author youjun
	 *@date 2017年8月7日 下午10:22:00
	 *@param map
	 *@return
	 * @throws ServiceException 
	 */
	private String getOperateCodeContent(final Map<String, Object> map, final Integer productId, final int codeLevel) throws ServiceException {
		
		if(CollectionUtils.isEmpty(map)||productId==null) {
			throw new ServiceException("参数错误");
		}
		@SuppressWarnings("unchecked")
		final List<MultistageLevelQRCode> outPackLevelCodeList = (List<MultistageLevelQRCode>) map.get("outPackLevelCodeList");
		@SuppressWarnings("unchecked")
		final List<FirstLevelQRCode> outSingleLevelCodeList = (List<FirstLevelQRCode>) map.get("outSingleLevelCodeList");
		final StringBuilder operateCodeContent = new StringBuilder();
		if(codeLevel>1) {
			if(!CollectionUtils.isEmpty(outPackLevelCodeList)){
				for(final MultistageLevelQRCode item:outPackLevelCodeList) {
					if(item.getProductId()==productId.intValue()){
						operateCodeContent.append(item.getPackContent()).append(",");
					}
				}
			}
		}else{
			if(!CollectionUtils.isEmpty(outSingleLevelCodeList)){
				for(final FirstLevelQRCode item:outSingleLevelCodeList) {
					if(item.getProductId()==productId.intValue()){
						operateCodeContent.append(item.getQrCode()).append(",");
					}
				}
			}
		}
		if(operateCodeContent.length()>0) {
			return operateCodeContent.substring(0, operateCodeContent.length()-1);
		}
		return "";
	}

	/**
	 *@Title: validateUpdate
	 *@Description: 出库上传操作前进行验证
	 *@return Map<String,Object> map中的key如下:
	 *	<ul>
	 *		<li>1、validateResponse:验证结果,验证通过后后续key才有</li>
	 *		<li>2、currentActualNumMap:"本次"实际出库数量(使用map,key为产品id,value为int数组,[单品数量,包装数量])</li>
	 *		<li>3、</li>
	 *	</ul>
	 *@author youjun
	 *@date 2017年8月6日 下午3:01:47
	 *@param outStorageVo
	 *@return
	 */
		final Map<String, Object> map = new HashMap<String, Object>();
		private Map<String, Object> validateUpdate(final OutStorageVo outStorageVo) throws ServiceException {
		try{
			// 第一步:参数基本验证
			Response validateResponse = validateParamsForUpdate(outStorageVo);
			final String enterpriseCode = outStorageVo.getEnterpriseCode();
			map.put("validateResponse", validateResponse); // 验证结果
			if(validateResponse.getCode()==Cons.RESPONSE_CODE_SUCCESS) {
				final OutStorage outStorage = outStorageDao.findById(outStorageVo.getOutStorageId(), enterpriseCode);
				if(outStorage==null){
					LOGGER.info("未找到相应的出库单");
					validateResponse = new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "未找到相应的出库单");
				}else{
					WarehouseData warehouseData = warehouseService.findById(outStorage.getWarehouseId(), enterpriseCode);
					if (Utils.isEmpty(warehouseData)) {
						LOGGER.info("当前出库单所属仓库不存在,不能执行该操作");
						validateResponse = new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "当前出库单所属仓库不存在,不能执行该操作");
					}
					//添加库存id信息
					outStorageVo.setWarehouseId(outStorage.getWarehouseId());
					outStorageVo.setWarehouseName(warehouseData.getWarehouseName());
					//添加客户类型
					outStorageVo.setCustomerType(outStorage.getCustomerType());
					LOGGER.info("获取的出库单信息为:{}", JSONObject.toJSONString(outStorage));
					if(outStorage.getStatus()==null) {
						LOGGER.info("当前出库单的状态未知,不能执行该操作");
						validateResponse = new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, "当前出库单的状态未知,不能执行该操作");
					}else{
						final Integer status = outStorage.getStatus().intValue();
						if(!canUpdateStatusList.contains(status)) {
							Map<Integer, String> statusMap = OutStorageStatusEnum.getAll();
							final StringBuilder errorMsg = new StringBuilder("当前出库单处于").append(statusMap.get(status));
							errorMsg.append("状态，不能执行该操作");
							LOGGER.info(errorMsg.toString());
							validateResponse = new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, errorMsg.toString());
						}
					}
					
				}
				if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
					LOGGER.info("验证未通过");
					map.put("validateResponse", validateResponse); // 验证结果
					return map;
				}
				// 第二步:判断出库状态以及当前用户是否有权限操作该出库单
				// validateResponse = ...
				// 第三步:判断码是否正确:1、是否存在2、瓶码不能存在关联关系3、码是否已经出库(根据码的流转记录判断)
				// 第四步:判断实际包装数量、单品数量是否超过计划包装数量、单品数量,正确返回实际数量
				validateResponse = validateCodeAndActualNum(outStorageVo, map, enterpriseCode);
				map.put("validateResponse", validateResponse); // 验证结果
			}
			
		}catch(Exception e){
			throw new ServiceException(e);
		}
		return map;
	}

	/**
	 *@Title: validateCodeAndActualNum
	 *@Description: 1、验证码是否正确(码是否正确:1、是否存在2、瓶码不能存在关联关系3、码是否已经出库(根据码的流转记录判断))<br>
	 *2、实际包装数量、单品数量是否超过计划包装数量、单品数量,正确将填充vo中的实际出库数量,并put入map中的
	 *	1）key为currentActualNumMap
	 *(本次实际出库数量,key为产品ID,value为int数组[实际出库单品数量,实际出库包装数量])
	 *	2）outPackLevelCodeList：本次出库的所有包装单位码集合,List<MultistageLevelQRCode>
	 *	3）outSingleLevelCodeList：本次出库的所有单品码集合,List<FirstLevelQRCode>
	 *@return void
	 *@author youjun
	 *@date 2017年8月7日 上午10:37:08
	 *@param outStorageVo
	 *@param map
	 *@param enterpriseCode
	 * @return 
	 *@throws ServiceException
	 */
	private Response validateCodeAndActualNum(final OutStorageVo outStorageVo, final Map<String, Object> map,
			final String enterpriseCode) throws ServiceException {
		final List<OutStorageDetailVo> detailVoList = outStorageVo.getDetailList();
		// 上面已做非空验证
		ProductData product = null;
		List<OperateCodeVo> operateCodeVoList = null; // 操作码集合
		OutStorageDetail outStorageDetail = null;
		Integer warehouseId = outStorageVo.getWarehouseId(); //仓储id
		try {
			// "本次"实际数量map,key为产品ID,value为[实际出库单品数量,实际出库包装数量]
			final Map<Integer, Integer[]> currentActualNumMap = new HashMap<Integer, Integer[]>();
			final List<QRCodeError> qRCodeErrorList = new ArrayList<QRCodeError>(); // 码的错误信息集合
			QRCodeError qRCodeError = null; // 码的错误信息
			int failNum = 0; // 失败的总包装码数
			int failBottle = 0; // 失败的总单品数
			// 出库的包装单位码和单品单位码
			final List<MultistageLevelQRCode> outPackLevelCodeList = new ArrayList<MultistageLevelQRCode>();
			final List<FirstLevelQRCode> outSingleLevelCodeList = new ArrayList<FirstLevelQRCode>();
			for(final OutStorageDetailVo detailVo:detailVoList) { // 入库单详情for循环开始
				// 查询产品信息
				product = productService.getProductFromCache(detailVo.getProductId(), enterpriseCode);
				if(product==null) {
					return new Response(Cons.RESPONSE_CODE_NO_DATA, "产品不存在"); // 验证结果
				}
				if(product.getProductUnit()==null) {
					return new Response(Cons.RESPONSE_CODE_NO_DATA, "产品未设置包装单位");
				}
				final Map<Integer, String> packStandardMap = product.getPackStandardMap(); // 包装map
				// 验证出库单详情
				final OutStorageDetail outStorageDetailCondition = new OutStorageDetail();
				outStorageDetailCondition.setOutStorageId(outStorageVo.getOutStorageId());
				outStorageDetailCondition.setProductId(detailVo.getProductId());
				final List<OutStorageDetail> outStorageDetailList = outStorageDetailDao.findListByCondition(outStorageDetailCondition , enterpriseCode);
				if(CollectionUtils.isEmpty(outStorageDetailList)) {
					return new Response(Cons.RESPONSE_CODE_NO_DATA, new StringBuilder("产品:").append(product.getProductName()).append("在出库单中不存在").toString());
				}
				if(outStorageDetailList.size()!=2) {
					return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, new StringBuilder("出库单详情产品数据不正确,当前产品:").append(product.getProductName()).toString());
				}
				final OutStorageDetail tempOutStorageDetail = outStorageDetailList.get(0);
				int tempPlanSpNum = 0;
				int tempActualSpNum = 0;
				if(tempOutStorageDetail.getCodeLevel()!=null&&tempOutStorageDetail.getCodeLevel().intValue()!=1) {
					outStorageDetail = tempOutStorageDetail;
					tempPlanSpNum = outStorageDetailList.get(1).getPlanNum()!=null?outStorageDetailList.get(1).getPlanNum():0;
					tempActualSpNum = outStorageDetailList.get(1).getActualNum()!=null?outStorageDetailList.get(1).getActualNum():0;
				}else{
					outStorageDetail = outStorageDetailList.get(1);
					tempPlanSpNum = tempOutStorageDetail.getPlanNum()!=null?outStorageDetailList.get(1).getPlanNum():0;
					tempActualSpNum = tempOutStorageDetail.getActualNum()!=null?outStorageDetailList.get(1).getActualNum():0;
				}
				// 填充计划、实际单品数量
				outStorageDetail.setPlanSpNum(tempPlanSpNum);
				outStorageDetail.setActualSpNum(tempActualSpNum);
				final Integer packLevel = product.getProductUnit().intValue(); // 包装级别
				final String singleUnit = product.getSingleUnit(); // 单品单位
				final String packUnit = product.getPackUnit(); // 包装单位
				// 当前产品本次单品数量
				int singleCount = 0;
				// 当前产品本次包装数量
				int packCount = 0;
				// 已出库的实际数量
				final int alreadyActualBottle = outStorageDetail.getActualSpNum()!=null?outStorageDetail.getActualSpNum():0;
				final int alreadyActualNum = outStorageDetail.getActualNum()!=null?outStorageDetail.getActualNum():0;
				int planBottle = outStorageDetail.getPlanSpNum()!=null?outStorageDetail.getPlanSpNum():0; // 计划单品数量
				
				int planNum = outStorageDetail.getPlanNum()!=null?outStorageDetail.getPlanNum():0; // 计划包装数量
				operateCodeVoList = detailVo.getOperateCodeList();
				StringBuilder errorMsg = null;
				if(!CollectionUtils.isEmpty(operateCodeVoList)) {
					for(final OperateCodeVo operateCodeVo:operateCodeVoList){
						final String operateCode = operateCodeVo.getCode(); // 操作码
						final Integer codeLevel = operateCodeVo.getLevel(); // 操作码的级别
						if(StringUtils.isBlank(operateCode)||codeLevel==null) {
							return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "操作码或级别不能为空");
						}
						if(codeLevel==1) { // 单品码
							final FirstLevelQRCode firstLevelQRCode = firstLevelQRCodeService.findFirstLevelQRCodeByQRCode(enterpriseCode, operateCode, false);
							outSingleLevelCodeList.add(firstLevelQRCode); // 记录出库的单品码
							// 验证单品码:非空、不包含父码、非出库状态,返回错误瓶数
							final int errorCount = firstLevelQRCodeService.validateSingleLevelCode(qRCodeErrorList, firstLevelQRCode, operateCode, product, true, 1);
							if(errorCount>0){
								failBottle+=errorCount;
							}else{
								// 单品码验证通过,累加出库的单品数量
								singleCount++;
							}
							
							
						}else{ // 多级码
							MultistageLevelQRCode multisQRCode = null;
							Map<String, Object> multisCodeMap = new HashMap<String, Object>();
							if(codeLevel>packLevel) { // 虚拟码,比如层、垛
								multisCodeMap = multistageLevelQRCodeService.getInventedCodeRelatedInfo(enterpriseCode, operateCode, codeLevel, false, packLevel, true);
								multisQRCode = (MultistageLevelQRCode) multisCodeMap.get("multistageLevelQRCode");
								if(multisQRCode==null) {
									errorMsg = new StringBuilder("码:");
									errorMsg.append(operateCode).append("不存在");
									LOGGER.info(errorMsg.toString());
//									return new Response(Cons.RESPONSE_CODE_NO_DATA, errorMsg.toString());
									qRCodeError = new QRCodeError(operateCode, null, product.getProductId(), errorMsg.toString());
									qRCodeErrorList.add(qRCodeError);
									// 获得当前虚拟码对应的包装数量
									final int errorPackCount = productService.getCodeHaveSpecifiedLevelCount(product, codeLevel, packLevel);
									failNum+=errorPackCount; // 失败数+转换成的包装数
								}else{
						/*			if(multisQRCode.getStatus()!=null
											&&alreadyOutStorageStatusList.contains(multisQRCode.getStatus())){
										qRCodeError = new QRCodeError(operateCode, null, product.getProductId(), errorMsg.toString());
										qRCodeErrorList.add(qRCodeError);
										failNum++; // 失败数+1
									}else{*/
										// 验证多级码下的所有包装码是否正确
										@SuppressWarnings("unchecked")
										final List<MultistageLevelQRCode> packMultistageLevelList = (List<MultistageLevelQRCode>) multisCodeMap.get("packMultistageLevelList");
										outPackLevelCodeList.addAll(packMultistageLevelList); // 记录出库的包装码
										if(CollectionUtils.isEmpty(packMultistageLevelList)){
											errorMsg = new StringBuilder(packStandardMap.get(codeLevel)).append("码:");
											errorMsg.append(operateCode).append("不存在下级码");
											qRCodeError = new QRCodeError(operateCode, codeLevel, product.getProductId(), errorMsg.toString());
											qRCodeErrorList.add(qRCodeError);
										}else{
											final List<QRCodeError> childErrorCode = new ArrayList<QRCodeError>();
											for(final MultistageLevelQRCode item:packMultistageLevelList) {
												// 判断每一个包装码是否为null、是否出库、是否满包装,返回错误包装数
												failNum+=multistageLevelQRCodeService.validatePackLevelCode(childErrorCode, item, "", product, false, 1, warehouseId,outStorageVo);
											}
											if(!CollectionUtils.isEmpty(childErrorCode)) {
												qRCodeError = new QRCodeError(operateCode, multisQRCode.getPackLevel(), product.getProductId(), packUnit+"码存在错误");
												qRCodeError.setChildErrorCode(childErrorCode);
												qRCodeErrorList.add(qRCodeError);
											}else{
												packCount+=(Integer)multisCodeMap.get("packCodeCount");
											}
										}
									//}
								}
							}else if(codeLevel==packLevel) { // 包装码,比如箱
								multisQRCode = multistageLevelQRCodeService.findMultistageLevelQRCodeByQRCodeAndLevel(enterpriseCode, operateCode, codeLevel, Cons.CODE_TYPE_STANDARD, false);
								outPackLevelCodeList.add(multisQRCode); // 记录入库的包装码
								final int errorCount = multistageLevelQRCodeService.validatePackLevelCode(qRCodeErrorList, multisQRCode, operateCode, product, true, 1, warehouseId,outStorageVo);
								if(errorCount>0){
									failNum+=errorCount;
								}else{
									packCount++; // 出库的包装数量+1
								}
							}else if(codeLevel > 1){ // 介于包装码和单品码之间的,比如盒
								multisCodeMap = multistageLevelQRCodeService.getInventedCodeRelatedInfo(enterpriseCode, operateCode, codeLevel, false, 1, true);
								multisQRCode = (MultistageLevelQRCode) multisCodeMap.get("multistageLevelQRCode");
								if(multisQRCode==null) {
									errorMsg = new StringBuilder("码:");
									errorMsg.append(operateCode).append("不存在");
									LOGGER.info(errorMsg.toString());
									qRCodeError = new QRCodeError(operateCode, codeLevel, product.getProductId(), errorMsg.toString());
									qRCodeErrorList.add(qRCodeError);
								}else if(StringUtils.isNotBlank(multisQRCode.getParentCode())){
									qRCodeError = new QRCodeError(operateCode, codeLevel, product.getProductId(), "存在上级关系，不能操作");
									qRCodeErrorList.add(qRCodeError);
								}
								// 暂时注释判断逻辑
								/*else if(alreadyOutStorageStatusList.contains(multisQRCode.getStatus())) {
									qRCodeError = new QRCodeError(operateCode, codeLevel, product.getProductId(), "码已出库");
									qRCodeErrorList.add(qRCodeError);
								}*/
								// 判断是否满包装
								final int packSubNum = productService.getCodeHaveSpecifiedLevelCount(product, codeLevel, codeLevel-1); // 包装规格规定的下级数量
								final int childNum = multisQRCode.getChildNum()==null?0:multisQRCode.getChildNum(); // 实际下级数量
								if(childNum!=packSubNum) {
									errorMsg = new StringBuilder(packStandardMap.get(codeLevel));
									errorMsg.append("码中包装的").append(packStandardMap.get(codeLevel-1)).append("不符合包装规格");
									qRCodeError = new QRCodeError(operateCode, codeLevel, product.getProductId(), errorMsg.toString());
									qRCodeErrorList.add(qRCodeError);
								}else{
									@SuppressWarnings("unchecked")
									final List<FirstLevelQRCode> firstQRCodeList = (List<FirstLevelQRCode>) multisCodeMap.get("packFirstLevelList");
									outSingleLevelCodeList.addAll(firstQRCodeList); // 记录出库的单品码
									if(CollectionUtils.isEmpty(firstQRCodeList)){
										errorMsg = new StringBuilder(packStandardMap.get(codeLevel)).append("码:");
										errorMsg.append(operateCode).append("不存在下级码");
										qRCodeError = new QRCodeError(operateCode, codeLevel, product.getProductId(), errorMsg.toString());
										qRCodeErrorList.add(qRCodeError);
									}else{
										final List<QRCodeError> childErrorCode = new ArrayList<QRCodeError>();
										for(final FirstLevelQRCode item:firstQRCodeList) {
											failBottle+=firstLevelQRCodeService.validateSingleLevelCode(childErrorCode, item, "", product, false, 1);
										}
										if(!CollectionUtils.isEmpty(childErrorCode)){
											qRCodeError = new QRCodeError(operateCode, multisQRCode.getPackLevel(), product.getProductId(), new StringBuilder(singleUnit).append("码存在错误").toString());
											qRCodeError.setChildErrorCode(childErrorCode);
											qRCodeErrorList.add(qRCodeError);
										}else{ // 正确
											singleCount+=(Integer)multisCodeMap.get("packCodeCount");
										}
									}
								}
							}
							
						}
					}
				}
				// 判断出库数量是否正确(不能大于入库数量)
				LOGGER.info("当前产品ID:{},产品name:{}本次的出库{}数:{},出库{}数:{}", product.getProductId(), 
						product.getProductName(), singleUnit, singleCount, 
						packUnit, packCount);
				final int totalSingleCount = singleCount+alreadyActualBottle;
				final int totalPackCount = packCount+alreadyActualNum;
				if(totalSingleCount>planBottle) {
					errorMsg = new StringBuilder("产品：").append(product.getProductName());
					errorMsg.append("出库数量已超出计划数量").append(totalSingleCount-planBottle).append(singleUnit).append("不能出库");
					LOGGER.error(errorMsg.toString());
					return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, errorMsg.toString());
				}
				if(totalPackCount>planNum) {
					errorMsg = new StringBuilder("产品：").append(product.getProductName());
					errorMsg.append("出库数量已超出计划数量").append(totalPackCount-planBottle).append(packUnit).append("不能出库");
					LOGGER.error(errorMsg.toString());
					return new Response(Cons.RESPONSE_CODE_DATA_EXCEPTION, errorMsg.toString());
				}
				// 判断码验证是否正确
				if(!CollectionUtils.isEmpty(qRCodeErrorList)) {
					final Map<String, Object> qRCodeErrorMap = new HashMap<>();
					qRCodeErrorMap.put("failNum", failNum); // 失败总箱
					qRCodeErrorMap.put("failBottle", failBottle); // 失败总瓶
					qRCodeErrorMap.put("codeInfo", QRCodeError.getNotRepetition(qRCodeErrorList)); // 失败码信息
					final Response errorCodeResponse = new Response(Cons.RESPONSE_CODE_STORAGR_SUMBIT_CODE_EXCEPTION, "码信息有误", qRCodeErrorMap);
					LOGGER.info("上传出库单接口传递的码数据有误,错误消息为:{}", JSONObject.toJSONString(errorCodeResponse));
					return errorCodeResponse;
				}
				// 存入本次操作成功的所有包装单位码和单品单位码
				map.put("outPackLevelCodeList", outPackLevelCodeList);
				map.put("outSingleLevelCodeList", outSingleLevelCodeList);
				currentActualNumMap.put(product.getProductId(), new Integer[]{singleCount, packCount});
				detailVo.setActualBottle(totalSingleCount);
				detailVo.setActualNum(totalPackCount);
				LOGGER.info("放入当前产品:{}的实际出库数量后的数据为:{}", product.getProductName(), JSONObject.toJSONString(detailVo));
			} // 入库单详情for循环结束
			LOGGER.info("出库单验证通过,放入各产品的实际出库数量:{}", JSONObject.toJSONString(currentActualNumMap));
			map.put("currentActualNumMap", currentActualNumMap);
		}catch(Exception e){
			LOGGER.error("上传出库单时验证出错", e);
			throw new ServiceException(e);
		}
		return new Response(ResponseEnum.SUCCESS);
	}

	/**
	 *@Title: validateParamsForUpdate
	 *@Description: 更新出库单参数验证
	 *@return Response
	 *@author youjun
	 *@date 2017年8月6日 下午2:37:59
	 *@param outStorageVo
	 *@return
	 */
	private Response validateParamsForUpdate(final OutStorageVo outStorageVo) {

		final Response validateBasicParams = super.validateBasicParams(outStorageVo, true, true);
		if(validateBasicParams.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
			return validateBasicParams;
		}
		if(outStorageVo==null) {
			return new Response(Cons.RESPONSE_CODE_PARAM_NULL, "出库单信息不能为空");
		}
		if(outStorageVo.getOutStorageId()==null) {
			return new Response(Cons.RESPONSE_CODE_PARAM_NULL, "出库单ID不能为空");
		}
		final List<OutStorageDetailVo> detailList = outStorageVo.getDetailList();
		if(CollectionUtils.isEmpty(detailList)) {
			return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "出库单中产品信息不能为空");
		}
		// 验证detailList的数据格式,[{"productId":产品ID,"operateCodeList":[{"code":"实体码","level":1},{},...]},{},...]
		List<OperateCodeVo> operateCodeVoList = null; // 操作的码的集合
		final Set<Integer> productSet = new HashSet<Integer>(); // 操作的产品,一个产品在出库单中只能出现一次
		for(final OutStorageDetailVo item:detailList) {
			operateCodeVoList = item.getOperateCodeList();
			if(item.getProductId()==null){
				return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "参数中产品ID不能为空");
			}
			if(!productSet.add(item.getProductId())) {
				return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "参数中出现了同一个产品");
			}
			if(!CollectionUtils.isEmpty(operateCodeVoList)) {
				for(final OperateCodeVo codeVo:operateCodeVoList) {
					if(StringUtils.isBlank(codeVo.getCode())) {
						return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "参数中未找到到码");
					}
					if(codeVo.getLevel()==null){
						return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "扫码的级别不能为空");
					}
				}
			}else{
				return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "参数中扫码信息不能为空");
			}
			
		}
		return new Response(ResponseEnum.SUCCESS);
	}

	/**(non-Javadoc)
	 *@Title: colse
	 *@Description: 
	 *@see com.sigmatrix.sc.handheld.inter.service.OutStorageService#colse(com.sigmatrix.sc.handheld.inter.web.vo.BaseOutStorageVo)
	 *@author youjun
	 *@date 2017年8月23日 下午4:06:18
	 */
	@Transactional(rollbackFor=ServiceException.class)
	@Override
	public Response colse(final BaseOutStorageVo baseOutStorageVo) throws ServiceException {

		final Response validateResponse = validateBasicParams(baseOutStorageVo);
		if(validateResponse.getCode()!=Cons.RESPONSE_CODE_SUCCESS) {
			return validateResponse;
		}
		final Long outStorageId = baseOutStorageVo.getOutStorageId();
		if(outStorageId==null) {
			return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, "出库单ID不能为空");
		}
		try {
			final String enterpriseCode = baseOutStorageVo.getEnterpriseCode();
			final OutStorage outStorage = outStorageDao.findById(outStorageId, enterpriseCode);
			if(outStorage==null) {
				return new Response(Cons.RESPONSE_CODE_NO_DATA, "该出库单不存在");
			}
			// 判断当前出库单状态能否执行关闭操作
			final Short status = outStorage.getStatus();
			if(status==null) {
				return new Response(Cons.RESPONSE_CODE_EXISTED_DATA, "当前出库单无状态,请检查");
			}
			if(!canCloseStatusList.contains(status.intValue())) {
				final Map<Integer, String> statusMap = OutStorageStatusEnum.getAll();
				return new Response(Cons.RESPONSE_CODE_PARAM_INVALID, new StringBuilder("当前出库单状态为:")
						.append(statusMap.get(status.intValue()))
						.append("不允许执行关闭操作").toString());
			}
			// 执行关闭操作
			outStorageDao.close(outStorageId, enterpriseCode);
			outStorageDetailDao.closeByOutStorageId(outStorageId, canCloseDetailStatusList, enterpriseCode);
			return new Response(ResponseEnum.SUCCESS);
		} catch (DaoException e) {
			throw new ServiceException(e);
		}
	}

	@Override
	public void upload(OutStorageVo outStorageVo) throws ServiceException {
		final int syncTaskId = outStorageVo.getSyncTaskId();
		final long startTime = System.currentTimeMillis();
		LOGGER.info("异步任务处理任务号为{}的出库单ID为{}开始...", syncTaskId, outStorageVo.getOutStorageId());
		final String enterpriseCode = outStorageVo.getEnterpriseCode();
		// 将任务置为处理中
		syncTaskService.updateStatus(enterpriseCode, syncTaskId, Cons.SYNC_TASK_STATUS_HANDLE_ING);
		// 开始处理
		final Response uploadResponse = update(outStorageVo);
		
		// 更新处理结果
		final SyncTask updateSyncTask = new SyncTask();
		final short isSuccess = uploadResponse.getCode().equals(Cons.RESPONSE_CODE_SUCCESS)?Cons.SYNC_TASK_SUCCESS:
			Cons.SYNC_TASK_FAIL;
		updateSyncTask.setIsSuccess(isSuccess);
		updateSyncTask.setResponseCode(uploadResponse.getCode()+"");
		updateSyncTask.setResponseMsg(uploadResponse.getMessage());
		updateSyncTask.setResponseResult(JSONObject.toJSONString(uploadResponse.getData()));
		updateSyncTask.setTypeSign(outStorageVo.getOrderCode());
		updateSyncTask.setStatus(Cons.SYNC_TASK_STATUS_HANDLE_FINISH); // 处理完成
		syncTaskService.updateByCondition(enterpriseCode, syncTaskId, 
				updateSyncTask);
		final long endTime = System.currentTimeMillis();
		LOGGER.info("异步任务处理任务号为{}的出库单ID为{}结束,花费时间:{}毫秒...", syncTaskId, 
				outStorageVo.getOutStorageId(), (endTime-startTime));
		
	}

	/**
	 * (non-Javadoc)
	 *@Title: findByOutStorageIdId
	 *@Description: 
	 *@see com.sigmatrix.h5.service.OutStorageService#findByOutStorageIdId(java.lang.Long, java.lang.String)
	 *@author gaomingkai
	 *@date 2018年1月11日 下午4:08:44
	 */
	@Override
	public OutStorage findByOutStorageIdId(Long id, String enterpriseCode) throws ServiceException {
		try {
			return outStorageDao.findById(id, enterpriseCode);
		} catch (DaoException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

}
